/*****************************************************************************
 *   Copyright 2005 Tim A Wang
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 ****************************************************************************/
package com.jb.web.renderer;

import com.jb.faces.el.AttributeEvaluator;
import com.jb.framework.datasource.TableModel;
import com.jb.web.bean.FormField;
import com.jb.web.bean.HtmlItem;
import com.jb.web.bean.MenuItem;
import com.jb.web.bean.UIComponentImpl;
import com.jb.web.bean.lookup.LookupBase;
import com.jb.web.context.WebContext;

import java.util.Iterator;

import com.jb.faces.application.FacesMessage;
import com.jb.faces.component.NamingContainer;
import com.jb.faces.component.UIComponent;
import com.jb.faces.context.FacesContext;
import com.jb.faces.render.Renderer;

import com.jb.web.bean.ValueHolderImpl;

import javax.servlet.jsp.el.ELException;


public abstract class RendererImpl implements Renderer {
    static final String SELECT_EVENT = "onClick";

    /**
     * This is action that will select the component in session, also open up a property inspector.
     *
     * @param ctx
     * @param htmlItem
     *
     * @return
     */
    public static String getEditBody(WebContext ctx, HtmlItem htmlItem) {
        if ((ctx != null) && !ctx.isRunTime() && htmlItem.isDesignable()) {
            return addTagProperty(SELECT_EVENT, getSelectAction(ctx, htmlItem), null) +
            addTagProperty("id", htmlItem.getClientId(), null) + getDropAction(htmlItem);
        } else {
            return getRuntimeEvent(ctx, htmlItem) + addTagProperty("id", htmlItem.getClientId(), null);
        }
    }


    public Object getConvertedValue(FacesContext ctx, UIComponent item, Object value) {
        System.out.println("get converted value " + value);

        return value;
    }

    /**
     * Determine if this field is editable.
     *
     * @param ctx
     * @param pItem
     *
     * @return
     */
    public boolean isEditable(WebContext ctx, UIComponentImpl pItem) {
        return !ctx.isRunTime() && pItem.isDesignable();
    }

    /**
     * Test if a string is null or "";
     *
     * @param obj
     *
     * @return
     */
    public static boolean isEmptyString(Object obj) {
        return (obj == null) || (obj.toString().trim().length() == 0);
    }

    public static String getRuntimeEvent(WebContext ctx, UIComponentImpl item) {
        if ((item.isDesignable() && !ctx.isRunTime()) && !item.getPage().getName().equals("toolMenu")) {
            return "";
        }

        if (!(item instanceof HtmlItem)) {
            return "";
        }

        HtmlItem pItem = (HtmlItem) item;
        String eventHandler = null;

        if (!isEmptyString(pItem.getOnDblClick())) {
            eventHandler = pItem.getOnDblClick();
        }

        StringBuffer html = new StringBuffer();
        html.append(addTagProperty("onClick", evaluate(ctx, pItem, pItem.getOnClick()), null))
            .append(addTagProperty("onDblClick", evaluate(ctx, pItem, eventHandler), null))
            .append(addTagProperty("onMouseOver", evaluate(ctx, pItem, pItem.getOnMouseOver()), null))
            .append(addTagProperty("onMouseOut", evaluate(ctx, pItem, pItem.getOnMouseOut()), null))
            .append(addTagProperty("onMouseDown", evaluate(ctx, pItem, pItem.getOnMouseDown()), null)).append(addTagProperty(
                "onMouseUp", evaluate(ctx, pItem, pItem.getOnMouseUp()), null));

        if (pItem instanceof FormField) {
            html.append(addTagProperty("onChange", "" + evaluate(ctx, pItem, ((FormField) pItem).getOnChange()), null));
        }
        return html.toString();
    }

    public static String getSelectAction(WebContext ctx, UIComponentImpl item) {
        if (ctx.isSelected(item)) {
            return ""; //if is selected, do not do it
        }

        return "jbDesigner.select(event, '" + ctx.getPageName() + ".xsp','" + item.getId() + "','" +
        item.getClientId() + "');";
    }

    public boolean getRendersChildren() {
        return true;
    }

    /**
     * Tests if a component is visible or not
     *
     */
    public boolean isVisible(WebContext ctx, UIComponent pItem) {
        UIComponentImpl item = (UIComponentImpl) pItem;

        return (!ctx.isRunTime() && item.isDesignable()) || "true".equals(evaluate(ctx, item, item.getVisible()));
    }

    public static String addTagProperty(String propName, String propValue, String defaultValue) {
        propName = " " + propName;

        if (isEmptyString(propValue)) {
            return (isEmptyString(defaultValue)) ? "" : (propName + "=\"" + defaultValue + "\"");
        } else {
            return propName + "=\"" + propValue + "\"";
        }
    }

    public static String evaluate(WebContext ctx, UIComponentImpl comp, Object expression) {
        if ((expression == null) || isEmptyString(expression.toString())) {
            return "";
        }

        try {
            String obj = (String)AttributeEvaluator.evaluate(ctx, comp, expression.toString(), String.class);

            if (isEmptyString(obj)) {
                return "";
            }

            return obj;
        } catch (ELException e) {
            ctx.addErrorMessage(comp, "Expression Error " + e.getMessage());

            return "" + expression;
        }
    }

    public static Object evaluateObject(WebContext ctx, UIComponentImpl comp, Object expression) {
        if (isEmptyString(expression)) {
            return null;
        }

        try {
            return com.jb.faces.el.AttributeEvaluator.evaluate(ctx, comp, "" + expression);
        } catch (ELException e) {
            ctx.addErrorMessage(comp, "Expression Error " + e.getMessage());
            return expression;
        }
    }

    /**
     * Returns a tableDataObject that is rendered as lov.
     *
     * @param ctx
     * @param pItem
     * @param pOptionString
     *
     * @return
     */
    public static TableModel optionData(WebContext ctx, ValueHolderImpl pItem, String pOptionString) {
        try {
            if (pOptionString == null || pOptionString.length()==0) {
                return null;
            }
            int i = pOptionString.indexOf(":");
            if (i < 0) {
                return null;
            }
            String dataSourceType = pOptionString.substring(0, i);

            return LookupBase.getLookupData(ctx, dataSourceType, pOptionString.substring(i + 1), pItem.getValueFormat());
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public String convertClientId(FacesContext ctx, String item) {
        return item;
    }

    //Faces implementation
    public void decode(FacesContext ctx, UIComponent item) {
        //no operations
    }
    
    public abstract StringBuffer renderBegin(WebContext ctx, UIComponent itemToRender);
    public abstract StringBuffer renderEnd(WebContext ctx, UIComponent itemToRender);    

    public void encodeBegin(FacesContext ctx, UIComponent item) {
        try {
            ctx.getResponseWriter().write(renderBegin((WebContext) ctx, item).toString());
        } catch (Exception e) {
            ctx.addMessage("Exception", new FacesMessage(item.getId(), e.getLocalizedMessage()));
        }
    }

    public void encodeChildren(FacesContext ctx, UIComponent item) {
        try {
            ctx.getResponseWriter().write(renderChildren((WebContext) ctx, item).toString());
        } catch (Exception e) {
            ctx.addMessage("Exception", new FacesMessage(item.getId(), e.getLocalizedMessage()));
        }
    }

    public void encodeEnd(FacesContext ctx, UIComponent item) {
        try {
            ctx.getResponseWriter().write(renderEnd((WebContext) ctx, item).toString());
        } catch (Exception e) {
            ctx.addMessage("Exception", new FacesMessage(item.getId(), e.getLocalizedMessage()));
        }
    }


    public StringBuffer render(WebContext ctx, UIComponent item) {
        return isVisible(ctx, item)
        ? renderBegin(ctx, item).append(renderChildren(ctx, item)).append(renderEnd(ctx, item)) : new StringBuffer();
    }

    //replace harmful text such as script
    public String safeText(String text) {
        return text;
    }

    /**
     * This is action that will select the component in session, also open up a property inspector.
     *
     * @param ctx
     * @param htmlItem
     *
     * @return
    */
    protected String getEditScript(WebContext ctx, UIComponentImpl htmlItem) {
        if ((ctx != null) && !ctx.isRunTime() && htmlItem.isDesignable()) {
            return addTagProperty(SELECT_EVENT, getSelectAction(ctx, htmlItem), null) +
            addTagProperty("id", htmlItem.getClientId(), null) + getDropAction(htmlItem);
        } else {
            return getRuntimeEvent(ctx, htmlItem) + addTagProperty("id", htmlItem.getClientId(ctx), null);
        }
    }

    //
    //render popup item event
    //

    /**
     * Client side action to popup a context menu
     *
     * @param ctx
     * @param host
     *
     * @return
     */
    protected String getPopupMenuAction(WebContext ctx, HtmlItem host) {
        Iterator iter = host.getChildrenByType(com.jb.web.bean.Menu.class).iterator();
        while (iter.hasNext()) {
            MenuItem mi = (MenuItem) iter.next();
            return " onMouseDown=\"showJBMenu(event," + mi.getId() + ",'popup');\"";
        }
        ctx.addMessage(host.getClientId(), new FacesMessage("No menu item found"));
        return "";
    }
    
    static private String getDropAction(UIComponentImpl pItem) {
        StringBuffer buffer = new StringBuffer(" designable=\"true\" ");

        if (!pItem.isFacet() && pItem instanceof NamingContainer) {
            buffer.append(" ondrop=\"true\" ");
        }
        return buffer.toString();
    }
}
